#include <bits/stdc++.h>
#include <iostream>
using namespace std;
typedef struct node
{
    int number, lname;
    char name[21], sex;
    double score[5]; // score[3]=平均成绩， score[4]=总成绩
    struct node *next;
} Node, *Stu;

const int lStu = sizeof(Node); // 节点长度

void initFile();                   // 初始化文件
void insertNode(Stu &H, Node stu); // 学生元素按总成绩单调不增插入链表
void cinNode(Stu &H);              // 输入学生元素
void coutNode(Stu &H);             // 直接输出学生链表
void saveNodeToFile(Stu &H);       // 保存学生链表到文件
void coutNodeFromFile();           // 读文件输出学生链表

int main()
{
    Stu H;
    initFile();
    cinNode(H);
    cout << "直接输出: " << endl;
    coutNode(H);
    saveNodeToFile(H);
    cout << "读文件输出: " << endl;
    coutNodeFromFile();
    return 0;
}

void initFile() // 初始化文件
{
    // 读写打开建立一个二进制文件，若文件不存在则建立该文件
    // 若文件存在则文件长度清为零，即该文件内容会消失,实现初始化
    FILE *fp = fopen("student.dat", "wb+");
    if (fp == NULL)
    {
        cout << "打开文件失败!";
        exit(1); // 异常退出
    }
    fclose(fp);
}

void insertNode(Stu &H, Node stu) // 学生元素按总成绩单调不增插入链表
{
    Node *p, *q, *r; // r前驱, p工作指针, q创结点
    r = H;
    p = H->next;
    while (p != NULL && p->score[4] > stu.score[4])
    {
        r = p;
        p = p->next;
    }
    q = new Node; // q插入当前节点之前
    *q = stu;
    q->next = p;
    r->next = q; // r->q->p
}

void cinNode(Stu &H) // 输入一组学生元素
{
    int n, i, j, index = 0;
    char temp[50], str[301];
    H = new Node; // 初始化头节点
    Node stu;     // 学生信息节点
    if (H == NULL)
    {
        cout << "无内存空间可分配" << endl;
        return;
    }
    H->next = NULL;

    cin >> n;  // 输入n个学生的信息
    cin.get(); // 读取多余的换行符
    for (j = 0; j < n; j++)
    {
        cin.getline(str, 301); // 读取整行字符
        for (i = 0; i <= 299; i++)
        {
            if ((str[i] == 'M' || str[i] == 'F') && (str[i + 2] >= '0' && str[i + 2] <= '9'))
            {
                index = i; // 获取性别字符所在索引
            }
        }
        strncpy(temp, str, 8);
        temp[8] = '\0';
        stu.number = atol(temp);               // 获取学号
        stu.lname = index - 2 - 8;             // 获取姓名长度
        strncpy(stu.name, str + 9, stu.lname); // 获取姓名
        stu.name[stu.lname] = '\0';
        strncpy(&stu.sex, str + index, 1); // 获取性别
        strncpy(temp, str + index + 2, 5);
        temp[5] = '\0';
        stu.score[0] = atof(temp); // 获取第1科成绩
        strncpy(temp, str + index + 2 + 6, 5);
        temp[5] = '\0';
        stu.score[1] = atof(temp); // 获取第2科成绩
        strncpy(temp, str + index + 2 + 6 + 6, 5);
        temp[5] = '\0';
        stu.score[2] = atof(temp);                                       // 获取第3科成绩
        stu.score[3] = (stu.score[0] + stu.score[1] + stu.score[2]) / 3; // 平均成绩
        stu.score[4] = stu.score[0] + stu.score[1] + stu.score[2];       // 总成绩
        insertNode(H, stu);
    }
}

void coutNode(Stu &H) // 直接输出学生链表
{
    Node *p;
    p = H->next;
    while (p != NULL)
    {
        printf("%d %d %s %c %.2lf %.2lf %.2lf %.2lf %.2lf\n",
               p->number, p->lname, p->name, p->sex,
               p->score[0], p->score[1], p->score[2], p->score[3], p->score[4]);
        p = p->next;
    }
}

void saveNodeToFile(Stu &H) // 保存学生链表到文件
{
    FILE *fp = fopen("student.dat", "rb+");
    if (fp == NULL)
    {
        cout << "打开文件失败!";
        exit(1);
    }

    Node *p;
    p = H->next;
    while (p != NULL)
    {
        // 一个学生信息节点作为一个数据块写入
        fwrite(p, lStu, 1, fp);
        p = p->next;
    }
    fclose(fp);
}

void coutNodeFromFile() // 读文件输出学生链表
{
    FILE *fp = fopen("student.dat", "rb");
    if (fp == NULL)
    {
        cout << "打开文件失败!";
        exit(1);
    }
    Node stu;
    while (fread(&stu, lStu, 1, fp))
    {
        printf("%d %d %s %c %.2lf %.2lf %.2lf %.2lf %.2lf\n",
               stu.number, stu.lname, stu.name, stu.sex,
               stu.score[0], stu.score[1], stu.score[2], stu.score[3], stu.score[4]);
    }
    fclose(fp);
}

/*
5
20140201 Zhao Li F 80.00 80.00 80.00
20140202 Lu Yao M 89.40 86.50 88.00
20140203 Qiao En M 72.50 85.60 78.50
20140204 Tian Ya M 88.50 68.60 94.00
20140205 Li Hua M 90.00 90.00 90.00
*/
